#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
通用工具模块
提供监控系统所需的各种实用工具函数
"""

import os
import sys
import logging
import json
import smtplib
import socket
from datetime import datetime
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from typing import Dict, Any, Optional, List
import threading

# 线程锁，用于安全打印
print_lock = threading.Lock()

def safe_print(*args, **kwargs):
    """线程安全的打印函数"""
    with print_lock:
        try:
            print(*args, **kwargs)
            sys.stdout.flush()
        except (UnicodeEncodeError, UnicodeDecodeError):
            # 处理编码问题
            safe_args = []
            for arg in args:
                if isinstance(arg, str):
                    safe_args.append(arg.encode('utf-8', errors='ignore').decode('utf-8'))
                else:
                    safe_args.append(str(arg))
            print(*safe_args, **kwargs)
            sys.stdout.flush()
        except Exception as e:
            print(f"打印错误: {e}")

def setup_logging(log_level: str = "INFO", log_file: Optional[str] = None, 
                  log_format: Optional[str] = None) -> logging.Logger:
    """
    设置日志配置
    
    Args:
        log_level: 日志级别
        log_file: 日志文件路径
        log_format: 日志格式
    
    Returns:
        配置好的logger对象
    """
    if log_format is None:
        log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    
    # 创建logger
    logger = logging.getLogger('universal-server-monitor')
    logger.setLevel(getattr(logging, log_level.upper()))
    
    # 清除现有的处理器
    logger.handlers.clear()
    
    # 创建格式器
    formatter = logging.Formatter(log_format)
    
    # 控制台处理器
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(getattr(logging, log_level.upper()))
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    # 文件处理器（如果指定了日志文件）
    if log_file:
        # 确保日志目录存在
        log_dir = os.path.dirname(log_file)
        if log_dir and not os.path.exists(log_dir):
            os.makedirs(log_dir, exist_ok=True)
        
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        file_handler.setLevel(getattr(logging, log_level.upper()))
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    
    return logger

def format_bytes(bytes_value: float, unit: str = 'auto') -> str:
    """
    格式化字节数为人类可读的格式
    
    Args:
        bytes_value: 字节数
        unit: 单位 ('auto', 'B', 'KB', 'MB', 'GB', 'TB')
    
    Returns:
        格式化后的字符串
    """
    if unit == 'auto':
        for unit_name in ['B', 'KB', 'MB', 'GB', 'TB']:
            if bytes_value < 1024.0:
                return f"{bytes_value:.1f} {unit_name}"
            bytes_value /= 1024.0
        return f"{bytes_value:.1f} PB"
    else:
        unit_map = {'B': 1, 'KB': 1024, 'MB': 1024**2, 'GB': 1024**3, 'TB': 1024**4}
        if unit in unit_map:
            return f"{bytes_value / unit_map[unit]:.1f} {unit}"
        else:
            return f"{bytes_value:.1f} B"

def format_percentage(value: float, decimal_places: int = 1) -> str:
    """
    格式化百分比
    
    Args:
        value: 百分比值
        decimal_places: 小数位数
    
    Returns:
        格式化后的百分比字符串
    """
    return f"{value:.{decimal_places}f}%"

def get_system_info() -> Dict[str, Any]:
    """
    获取系统基本信息
    
    Returns:
        系统信息字典
    """
    import platform
    import psutil
    
    try:
        boot_time = datetime.fromtimestamp(psutil.boot_time())
        uptime = datetime.now() - boot_time
        
        return {
            'hostname': socket.gethostname(),
            'platform': platform.platform(),
            'system': platform.system(),
            'release': platform.release(),
            'version': platform.version(),
            'machine': platform.machine(),
            'processor': platform.processor(),
            'python_version': platform.python_version(),
            'boot_time': boot_time.isoformat(),
            'uptime_seconds': int(uptime.total_seconds()),
            'uptime_human': str(uptime).split('.')[0],  # 去掉微秒
            'cpu_count': psutil.cpu_count(),
            'cpu_count_logical': psutil.cpu_count(logical=True)
        }
    except Exception as e:
        return {'error': f'获取系统信息失败: {e}'}

def check_network_connectivity(host: str = "8.8.8.8", port: int = 53, timeout: int = 3) -> bool:
    """
    检查网络连接性
    
    Args:
        host: 目标主机
        port: 目标端口
        timeout: 超时时间（秒）
    
    Returns:
        是否连接成功
    """
    try:
        socket.setdefaulttimeout(timeout)
        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
        return True
    except Exception:
        return False

def send_email(smtp_config: Dict[str, Any], subject: str, body: str, 
               recipients: List[str], html_body: Optional[str] = None) -> bool:
    """
    发送邮件
    
    Args:
        smtp_config: SMTP配置
        subject: 邮件主题
        body: 邮件正文（纯文本）
        recipients: 收件人列表
        html_body: HTML格式邮件正文（可选）
    
    Returns:
        是否发送成功
    """
    try:
        # 创建邮件对象
        msg = MIMEMultipart('alternative')
        msg['From'] = smtp_config['sender']
        msg['To'] = ', '.join(recipients)
        msg['Subject'] = Header(subject, 'utf-8')
        
        # 添加纯文本内容
        text_part = MIMEText(body, 'plain', 'utf-8')
        msg.attach(text_part)
        
        # 添加HTML内容（如果提供）
        if html_body:
            html_part = MIMEText(html_body, 'html', 'utf-8')
            msg.attach(html_part)
        
        # 连接SMTP服务器并发送邮件
        with smtplib.SMTP(smtp_config['server'], smtp_config['port']) as server:
            if smtp_config.get('use_tls', True):
                server.starttls()
            
            if smtp_config.get('username') and smtp_config.get('password'):
                server.login(smtp_config['username'], smtp_config['password'])
            
            server.send_message(msg)
        
        return True
    except Exception as e:
        logging.error(f"发送邮件失败: {e}")
        return False

def validate_email_config(config: Dict[str, Any]) -> tuple[bool, str]:
    """
    验证邮件配置
    
    Args:
        config: 邮件配置字典
    
    Returns:
        (是否有效, 错误信息)
    """
    required_fields = ['server', 'port', 'sender']
    
    for field in required_fields:
        if field not in config:
            return False, f"缺少必需字段: {field}"
    
    # 验证端口号
    try:
        port = int(config['port'])
        if not (1 <= port <= 65535):
            return False, "端口号必须在1-65535之间"
    except (ValueError, TypeError):
        return False, "端口号必须是有效的整数"
    
    # 验证邮箱格式
    import re
    email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    if not re.match(email_pattern, config['sender']):
        return False, "发件人邮箱格式无效"
    
    return True, ""

def create_html_table(headers: List[str], rows: List[List[str]], 
                      table_class: str = "monitor-table") -> str:
    """
    创建HTML表格
    
    Args:
        headers: 表头列表
        rows: 数据行列表
        table_class: 表格CSS类名
    
    Returns:
        HTML表格字符串
    """
    html = f'<table class="{table_class}">\n'
    
    # 表头
    html += '  <thead>\n    <tr>\n'
    for header in headers:
        html += f'      <th>{header}</th>\n'
    html += '    </tr>\n  </thead>\n'
    
    # 表体
    html += '  <tbody>\n'
    for row in rows:
        html += '    <tr>\n'
        for cell in row:
            html += f'      <td>{cell}</td>\n'
        html += '    </tr>\n'
    html += '  </tbody>\n'
    
    html += '</table>\n'
    return html

def load_json_file(file_path: str) -> Optional[Dict[str, Any]]:
    """
    加载JSON文件
    
    Args:
        file_path: JSON文件路径
    
    Returns:
        解析后的字典，失败时返回None
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        logging.error(f"加载JSON文件失败 {file_path}: {e}")
        return None

def save_json_file(data: Dict[str, Any], file_path: str) -> bool:
    """
    保存数据到JSON文件
    
    Args:
        data: 要保存的数据
        file_path: JSON文件路径
    
    Returns:
        是否保存成功
    """
    try:
        # 确保目录存在
        os.makedirs(os.path.dirname(file_path), exist_ok=True)
        
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        return True
    except Exception as e:
        logging.error(f"保存JSON文件失败 {file_path}: {e}")
        return False

def get_timestamp(format_str: str = "%Y-%m-%d %H:%M:%S") -> str:
    """
    获取当前时间戳
    
    Args:
        format_str: 时间格式字符串
    
    Returns:
        格式化的时间字符串
    """
    return datetime.now().strftime(format_str)

def ensure_directory(directory: str) -> bool:
    """
    确保目录存在，不存在则创建
    
    Args:
        directory: 目录路径
    
    Returns:
        是否成功
    """
    try:
        os.makedirs(directory, exist_ok=True)
        return True
    except Exception as e:
        logging.error(f"创建目录失败 {directory}: {e}")
        return False

def get_file_size(file_path: str) -> Optional[int]:
    """
    获取文件大小
    
    Args:
        file_path: 文件路径
    
    Returns:
        文件大小（字节），失败时返回None
    """
    try:
        return os.path.getsize(file_path)
    except Exception:
        return None

def is_port_open(host: str, port: int, timeout: int = 3) -> bool:
    """
    检查端口是否开放
    
    Args:
        host: 主机地址
        port: 端口号
        timeout: 超时时间（秒）
    
    Returns:
        端口是否开放
    """
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            sock.settimeout(timeout)
            result = sock.connect_ex((host, port))
            return result == 0
    except Exception:
        return False

class ColoredOutput:
    """彩色输出工具类"""
    
    # ANSI颜色代码
    COLORS = {
        'red': '\033[91m',
        'green': '\033[92m',
        'yellow': '\033[93m',
        'blue': '\033[94m',
        'magenta': '\033[95m',
        'cyan': '\033[96m',
        'white': '\033[97m',
        'reset': '\033[0m',
        'bold': '\033[1m',
        'underline': '\033[4m'
    }
    
    @classmethod
    def colored(cls, text: str, color: str = 'white', bold: bool = False, 
                underline: bool = False) -> str:
        """
        返回带颜色的文本
        
        Args:
            text: 文本内容
            color: 颜色名称
            bold: 是否加粗
            underline: 是否下划线
        
        Returns:
            带ANSI颜色代码的文本
        """
        if not sys.stdout.isatty():
            return text  # 非终端环境不使用颜色
        
        result = ""
        if bold:
            result += cls.COLORS['bold']
        if underline:
            result += cls.COLORS['underline']
        if color in cls.COLORS:
            result += cls.COLORS[color]
        
        result += text + cls.COLORS['reset']
        return result
    
    @classmethod
    def success(cls, text: str) -> str:
        """绿色成功文本"""
        return cls.colored(text, 'green', bold=True)
    
    @classmethod
    def warning(cls, text: str) -> str:
        """黄色警告文本"""
        return cls.colored(text, 'yellow', bold=True)
    
    @classmethod
    def error(cls, text: str) -> str:
        """红色错误文本"""
        return cls.colored(text, 'red', bold=True)
    
    @classmethod
    def info(cls, text: str) -> str:
        """蓝色信息文本"""
        return cls.colored(text, 'blue')